package com.javaxiaobear.base.framework.security.service;

import cn.dev33.satoken.stp.StpUtil;
import com.javaxiaobear.base.common.constant.CacheConstants;
import com.javaxiaobear.base.common.constant.Constants;
import com.javaxiaobear.base.common.constant.UserConstants;
import com.javaxiaobear.base.common.exception.ServiceException;
import com.javaxiaobear.base.common.exception.user.BlackListException;
import com.javaxiaobear.base.common.exception.user.CaptchaException;
import com.javaxiaobear.base.common.exception.user.CaptchaExpireException;
import com.javaxiaobear.base.common.exception.user.UserNotExistsException;
import com.javaxiaobear.base.common.exception.user.UserPasswordNotMatchException;
import com.javaxiaobear.base.common.utils.DateUtils;
import com.javaxiaobear.base.common.utils.MessageUtils;
import com.javaxiaobear.base.common.utils.StringUtils;
import com.javaxiaobear.base.common.utils.ip.IpUtils;
import com.javaxiaobear.base.framework.manager.AsyncManager;
import com.javaxiaobear.base.framework.manager.factory.AsyncFactory;
import com.javaxiaobear.base.framework.redis.RedisCache;
import com.javaxiaobear.base.framework.security.BadCredentialsException;
import com.javaxiaobear.base.framework.security.LoginUser;
import com.javaxiaobear.module.system.domain.SysUser;
import com.javaxiaobear.module.system.service.ISysConfigService;
import com.javaxiaobear.module.system.service.ISysUserService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

/**
 * 登录校验方法
 *
 * @author javaxiaobear
 */
@Component
@Slf4j
public class SysLoginService {
  @Resource TokenService tokenService;

  @Resource RedisCache redisCache;

  @Resource ISysUserService userService;

  @Resource ISysConfigService configService;

  @Resource UserDetailsService userDetailsService;

  /**
   * 登录验证
   *
   * @param username 用户名
   * @param password 密码
   * @param code 验证码
   * @param uuid 唯一标识
   * @return 结果
   */
  public String login(String username, String password, String code, String uuid) {
    // 验证码校验
    validateCaptcha(username, code, uuid);
    // 登录前置校验
    loginPreCheck(username, password);
    LoginUser loginUser;
    try {
      loginUser = userDetailsService.login(username, password);
    } catch (Exception e) {
      if (e instanceof BadCredentialsException) {
        AsyncManager.me()
            .execute(
                AsyncFactory.recordLogininfor(
                    username,
                    Constants.LOGIN_FAIL,
                    MessageUtils.message("user.password.not.match")));
        throw new UserPasswordNotMatchException();
      } else {
        log.error("登录出错", e);
        AsyncManager.me()
            .execute(AsyncFactory.recordLogininfor(username, Constants.LOGIN_FAIL, e.getMessage()));
        throw new ServiceException(e.getMessage());
      }
    }
    AsyncManager.me()
        .execute(
            AsyncFactory.recordLogininfor(
                username, Constants.LOGIN_SUCCESS, MessageUtils.message("user.login.success")));
    recordLoginInfo(loginUser.getUserId());
    // 生成token
    return StpUtil.getTokenValue();
  }

  /**
   * 校验验证码
   *
   * @param username 用户名
   * @param code 验证码
   * @param uuid 唯一标识
   * @return 结果
   */
  public void validateCaptcha(String username, String code, String uuid) {
    boolean captchaEnabled = configService.selectCaptchaEnabled();
    if (captchaEnabled) {
      String verifyKey = CacheConstants.CAPTCHA_CODE_KEY + StringUtils.nvl(uuid, "");
      String captcha = redisCache.getCacheObject(verifyKey);
      redisCache.deleteObject(verifyKey);
      if (captcha == null) {
        AsyncManager.me()
            .execute(
                AsyncFactory.recordLogininfor(
                    username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.expire")));
        throw new CaptchaExpireException();
      }
      if (!code.equalsIgnoreCase(captcha)) {
        AsyncManager.me()
            .execute(
                AsyncFactory.recordLogininfor(
                    username, Constants.LOGIN_FAIL, MessageUtils.message("user.jcaptcha.error")));
        throw new CaptchaException();
      }
    }
  }

  /**
   * 登录前置校验
   *
   * @param username 用户名
   * @param password 用户密码
   */
  public void loginPreCheck(String username, String password) {
    // 用户名或密码为空 错误
    if (StringUtils.isEmpty(username) || StringUtils.isEmpty(password)) {
      AsyncManager.me()
          .execute(
              AsyncFactory.recordLogininfor(
                  username, Constants.LOGIN_FAIL, MessageUtils.message("not.null")));
      throw new UserNotExistsException();
    }
    // 密码如果不在指定范围内 错误
    if (password.length() < UserConstants.PASSWORD_MIN_LENGTH
        || password.length() > UserConstants.PASSWORD_MAX_LENGTH) {
      AsyncManager.me()
          .execute(
              AsyncFactory.recordLogininfor(
                  username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
      throw new UserPasswordNotMatchException();
    }
    // 用户名不在指定范围内 错误
    if (username.length() < UserConstants.USERNAME_MIN_LENGTH
        || username.length() > UserConstants.USERNAME_MAX_LENGTH) {
      AsyncManager.me()
          .execute(
              AsyncFactory.recordLogininfor(
                  username, Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
      throw new UserPasswordNotMatchException();
    }
    // IP黑名单校验
    String blackStr = configService.selectConfigByKey("sys.login.blackIPList");
    if (IpUtils.isMatchedIp(blackStr, IpUtils.getIpAddr())) {
      AsyncManager.me()
          .execute(
              AsyncFactory.recordLogininfor(
                  username, Constants.LOGIN_FAIL, MessageUtils.message("login.blocked")));
      throw new BlackListException();
    }
  }

  /**
   * 记录登录信息
   *
   * @param userId 用户ID
   */
  public void recordLoginInfo(Long userId) {
    SysUser sysUser = new SysUser();
    sysUser.setUserId(userId);
    sysUser.setLoginIp(IpUtils.getIpAddr());
    sysUser.setLoginDate(DateUtils.getNowDate());
    userService.updateUserProfile(sysUser);
  }
}
